/*
 *only support for (SIZEX-2)%BLKX == 0 &&
 *(SIZEY-2)%BLKY == 0(SIZEZ-2)%BLKZ == 0
 *
 */



#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <sys/time.h>
#include "athread.h"

double A[2][SIZEX][SIZEY][SIZEZ];
double test[2][SIZEX][SIZEY][SIZEZ];
int iter, N; ////n is the num of tiles that each thread assigned
int thread_num, ntilex, ntiley, ntilez;
int starti[64];
int startj[64];
int startk[64];

#define alpha_d 0.0876
#define beta_d 0.0765
#define TestDo3d7pkernel(itr,i,j,k) {\
   test[itr%2][i][j][k] = alpha_d * test[(itr+1)%2][i][j][k] + \
                        beta_d * (test[(itr+1)%2][i-1][j][k] + test[(itr+1)%2][i+1][j][k] +\
                        test[(itr+1)%2][i][j-1][k] + test[(itr+1)%2][i][j+1][k] +\
                        test[(itr+1)%2][i][j][k-1] + test[(itr+1)%2][i][j][k+1] ); } 
#define my_ceil(a,b) \
     (a%b==0)?a/b:a/b+1
extern SLAVE_FUN(func)();

int check(const int iter)
{
   int i,j,k;
   int itr;
   printf("begin check\n");
    for(i = 0; i < SIZEX; i++)
     for( j = 0; j < SIZEY; j++)
      for( k = 0; k < SIZEZ; k++){
         test[1][i][j][k] = (double)(i*2.5 + j*3.3 + k*0.5 ) /3.0; 
      }
    for(itr = 0; itr < iter; ++itr)
     for(i = 1; i < SIZEX - 1; i++)
      for( j = 1; j < SIZEY - 1; j++)
       for( k = 1; k < SIZEZ - 1; k++)
         TestDo3d7pkernel(itr,i,j,k);
       /*  if(i == 3 && j == 1 && k == 9)
     printf("master : %20.20f,%20.20f,%20.20f,%20.20f,%20.20f,%20.20f,%20.20f\n",test[(itr+1)%2][i][j][k],test[(itr+1)%2][i-1][j][k] ,test[(itr+1)%2][i+1][j][k],test[(itr+1)%2][i][j-1][k] ,test[(itr+1)%2][i][j+1][k],test[(itr+1)%2][i][j][k-1],test[(itr+1)%2][i][j][k+1]);}*/
    for(i = 1; i < SIZEX - 1; i++)
      for(j = 1; j < SIZEY - 1; j++)
       for(k = 1; k < SIZEZ - 1; k++){
          if( fabs(test[(iter+1)%2][i][j][k]  - A[(iter+1)%2][i][j][k]) > 1e-13 ){
             printf("i: %d   j:  %d k: %d\n",i,j,k);    
             printf("test: %20.20f",test[(iter+1)%2][i][j][k]);
             printf("\nA: %20.20f",A[(iter+1)%2][i][j][k]);
             printf("\nno");               
             return -1;
         }
      }
  printf("correct\n");
  return 0;
}


void init()
{
 int i,j,k;
 for(i = 0; i < SIZEX; i++)
  for(j = 0; j < SIZEY; j++)
   for(k = 0; k < SIZEZ; k++)
   {
     A[1][i][j][k] = (double)(i*2.5 + j*3.3 + k*0.5 ) /3.0;
   }
}

int main(int argc, char** argv)
{
    int ITER;
    int dest,src;
    int i,j,k,t;
    struct timeval start;
    struct timeval end;
    double total_time;  
        
    ITER = 100;
    if(argc > 1)
        thread_num = atoi(argv[1]);
    if(argc>2)
        ITER=atoi(argv[2]); 
    init();
    printf("iter=%d \n", ITER);
    athread_init();
    {
      int id;//thread id;
      int task_num;
      id = 0;
      N = (((SIZEX-2)/BLKX)*((SIZEY-2)/BLKY)*((SIZEZ-2)/BLKZ))/thread_num;
      ntilex = my_ceil((SIZEX-2),BLKX);
      ntiley = my_ceil((SIZEY-2),BLKY);
      ntilez = my_ceil((SIZEZ-2),BLKZ);
      //init the start addr of task  
      task_num = 0;
      starti[0] = 0;
      startj[0] = 0;
      startk[0] = 0;
      for(i = 0; i < (SIZEX-2)-BLKX+1; i += BLKX)
        for(j = 0; j < (SIZEY-2)-BLKY+1; j += BLKY)
          for(k = 0; k < (SIZEZ-2)-BLKZ+1; k += BLKZ)
          { 
            ++task_num;
            if(task_num == N+1)
            {
              ++id;
              starti[id] = i;
              startj[id] = j;
              startk[id] = k; 
              task_num = 1;
            }
          }//
    } //finish partition tiles
      
    gettimeofday(&start,NULL);
    for(iter=0; iter<ITER; iter++)
    {
      for(t = 0; t < thread_num; ++t)
        athread_create(t, func, 0);
      for(t = 0; t < thread_num; ++t)
        athread_wait(t);
    }
    gettimeofday(&end,NULL);
    for(t = 0; t < thread_num; ++t)
        athread_end(t);
    
    total_time = 0.0;
    total_time+=(end.tv_sec-start.tv_sec)+(end.tv_usec-start.tv_usec)/1000000.0;
    printf("total_time: %lf\n",total_time);     
#ifdef CHECK
    if(check(ITER))
    {
        printf(" error\n");
        exit(1);
    }
#endif
    return 0;
}
         
